/*
 * Copyright (c) from 2000 to 2009
 * 
 * Network and System Laboratory 
 * Department of Computer Science 
 * College of Computer Science
 * National Chiao Tung University, Taiwan
 * All Rights Reserved.
 * 
 * This source code file is part of the NCTUns 6.0 network simulator.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation is hereby granted (excluding for commercial or
 * for-profit use), provided that both the copyright notice and this
 * permission notice appear in all copies of the software, derivative
 * works, or modified versions, and any portions thereof, and that
 * both notices appear in supporting documentation, and that credit
 * is given to National Chiao Tung University, Taiwan in all publications 
 * reporting on direct or indirect use of this code or its derivatives.
 *
 * National Chiao Tung University, Taiwan makes no representations 
 * about the suitability of this software for any purpose. It is provided 
 * "AS IS" without express or implied warranty.
 *
 * A Web site containing the latest NCTUns 6.0 network simulator software 
 * and its documentations is set up at http://NSL.csie.nctu.edu.tw/nctuns.html.
 *
 * Project Chief-Technology-Officer
 * 
 * Prof. Shie-Yuan Wang <shieyuan@csie.nctu.edu.tw>
 * National Chiao Tung University, Taiwan
 *
 * 09/01/2009
 */

#include "net_entry_mngr.h"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <limits.h>
#include <nctuns_api.h>

#include "frame_mngr.h"
#include "mac802_16_meshss.h"
#include "mesh_connection.h"
#include "mesh_route.h"
#include "msh_ncfg.h"
#include "msh_ncfg/emb_ie_net_entry_open.h"
#include "msh_ncfg/emb_ie_net_entry_ack.h"
#include "msh_ncfg/emb_ie_link_est.h"
#include "msh_nent.h"
#include "neighbor_mngr.h"
#include "network_des.h"
#include "node_deg_des.h"
#include "sch/ctrl/scheduler.h"
#include "sch/data/scheduler.h"
#include "util_map.h"
#include "../library.h"
#include "../mac_address.h"
#include "../timer_mngr.h"


#define VERBOSE_LEVEL   MSG_INFO
#include "../verbose.h"


/*
 * Member function definitions of class `Net_entry_mngr'.
 */

/*
 * Constructor
 */
Net_entry_mngr::Net_entry_mngr(mac802_16_MeshSS* mac)
: _mac(mac)
, _link_est_ie_format(Msh_ncfg::original_std)
/*
 * Network entry process mode setting.
 */
#if 0
, _network_entry_process_mode(original_nent_process_mode)
#else
, _network_entry_process_mode(enhanced_nent_process_mode)
#endif
/*
 * Link establishment mode setting.
 */
#if 0
, _link_est_protocol_mode(original_link_est_mode)
#else
, _link_est_protocol_mode(extended_link_est_mode)
#endif
, _state(NET_ENTRY_INIT)
, _sync_hop_count(0xffff)
, sponsoringState(sprNone)

, isSponsoring(false)
, ncfg_entry_open_ie_p(NULL)
, sponsoring_retry_cnt(0)
, default_max_sponsoring_retry_cnt(5)

, sponsorsState(sprNone)
, targetSponsor(NULL)
, seek_for_sponsor_retry_cnt(0)
, default_max_seek_for_sponsor_retry_cnt(5)
, _use_nexthop_to_bs_as_sponsor_node_flag(true)
/* The change_route_to_bs_flag turns on/off the mechanism that
 * dynamically changes route to BS nodes for each SS node. In such
 * a way, routes generated by static route assignment schemes can
 * still find better routing paths to BS nodes by changing routes
 * during simulation. Otherwise, a routing loop may occur when
 * static routing schemes are used.
 */
, _change_route_to_bs_flag(false)

, _nent_log(new Nent_and_link_est_log())
, _link_est_log(new Nent_and_link_est_log())
{
}


/*
 * Destructor
 */
Net_entry_mngr::~Net_entry_mngr()
{
    if (_link_est_log)
        delete _link_est_log;

    if (_nent_log)
        delete _nent_log;

    /*
     * Recycle transmission links.
     */
    for (Mesh_link_tx::hash_t::iterator it = _tx_links.begin();
            it != _tx_links.end(); it++)
        delete it->second;
}

bool
Net_entry_mngr::is_sponsoring()
{
#if 0
    if (!isSponsoring) {
        u_char mac_zero[] = {0, 0, 0, 0, 0, 0};
        assert(memcmp(mac_zero, sponsoredMacAddr, 6) == 0);
    }
#endif

    return isSponsoring;
}

void
Net_entry_mngr::show_net_entry_mode()
{
    const char* mode;
    if (_network_entry_process_mode == original_nent_process_mode)
        mode = "original_nent_process_mode";
    else if (_network_entry_process_mode == enhanced_nent_process_mode)
        mode = "enhanced_nent_process_mode";
    else
        FATAL("detect undefined mode.\n");

    STAT("[%03u]%s: \e[1;33muse %s\e[m\n", _mac->get_nid(), __func__, mode);
}

void
Net_entry_mngr::show_link_est_mode()
{
    const char* mode;
    if (_link_est_protocol_mode == original_link_est_mode)
        mode = "original_link_est_mode";
    else if (_link_est_protocol_mode == extended_link_est_mode)
        mode = "extended_link_est_mode";
    else
        FATAL("Link Establishement Mode: detect undefined mode.\n");

    STAT("[%03u]%s: \e[1;33muse %s\e[m\n", _mac->get_nid(), __func__, mode);
}

Msh_ncfg::link_est_ie_format_t
Net_entry_mngr::set_link_est_ie_format()
{
    switch (_link_est_protocol_mode) {

    case original_link_est_mode:

        _link_est_ie_format = Msh_ncfg::original_std;
        DEBUG("link_est_ie_format = original_std\n");
        break;

    case extended_link_est_mode:

        _link_est_ie_format = Msh_ncfg::extended;
        DEBUG("link_est_ie_format = extended\n");
        break;

    default:
        FATAL("undefined mode.\n");
    }

    return _link_est_ie_format;
}

void
Net_entry_mngr::t25()
{
    const Frame_mngr* fr_mngr = _mac->fr_mngr();

    _next_nent_attempt_opp = fr_mngr->nent_opp_seq_end() + 1 + (random() % 128);

    if (_next_nent_attempt_opp > fr_mngr->max_nent_tx_opp())
        _next_nent_attempt_opp %= fr_mngr->max_nent_tx_opp();

    INFO("[%03u]%s: nent_opp_seq_end = %u, next_nent_attempt_opp = %u, max_nent_tx_opp = %u\n",
            _mac->get_nid(), __func__,
            fr_mngr->nent_opp_seq_end(), _next_nent_attempt_opp, fr_mngr->max_nent_tx_opp());
}

const MSH_NENT*
Net_entry_mngr::dequeue_nent()
{
    /*
     * Part of the NetEntry scheduling protocol.
     */
    bool ready_to_tx = false;

    /* T25 Timeout */
    if (_next_nent_attempt_opp == _mac->fr_mngr()->nent_opp_seq_end()) {

        // Retry
        if (_state == NET_ENTRY_OPEN) {

            ++seek_for_sponsor_retry_cnt;

            if (seek_for_sponsor_retry_cnt >= default_max_seek_for_sponsor_retry_cnt ) {

                /* change the candidate sponsor node to another one. */
                targetSponsor = _select_sponsor();

                INFO("[%03u]%s: Change targetSponsor = %d\n",
                        _mac->get_nid(), __func__, targetSponsor?targetSponsor->node_id():-1);

            }

            _enqueue_nent(MSH_NENT::NET_ENTRY_REQUEST);
            INFO("[%03u]%s: New Node: Retry the Network Entry procedure, next_nent_attempt_opp = %d\n",
                    _mac->get_nid(), __func__, _next_nent_attempt_opp);

            ready_to_tx = true; /* we need to turn on this flag to send an MSHNENT message out. */
        }
        else {

            if (is_sponsoring()) {

                /* Re-send Network Entry Ack IE. Move this action to SendMSHNCFG(). */
                /* INFO("Sponsor Node: Resend the Network Entry Open IE at next_nent_attempt_opp = %d\n",
                   next_nent_attempt_opp);*/
            }
            else {

                /* Re-send Network Entry Close IE */
                INFO("[%03u]%s: New Node: Resend the Network Entry Close IE at next_nent_attempt_opp = %d\n",
                        _mac->get_nid(), __func__, _next_nent_attempt_opp);

                _enqueue_nent(MSH_NENT::NET_ENTRY_CLOSE);
                ready_to_tx = true;
            }
        }

        /*
         * C.C. Lin:
         * restore the next_nent_attempt_opp, otherwise it will be triggered again.
         */
        if (is_sponsoring()) {
            ; /* the cancelation of T25 for a sponsoring node cannot be done at this point.*/
        }
        else
            _next_nent_attempt_opp = 0;
    }

    DEBUG("sponsorsState = %d, othersState = %d\n",
            sponsorsState, othersState);

    if (!_nent_tx_queue.empty()) {

        if (sponsorsState == sprAvailable) {

            if (othersState != sprBusy) {

                ready_to_tx = true;
            }

        }
        else if (sponsorsState == sprPolling) {

            if (othersState != sprBusy) {

                ready_to_tx = true;
            }
            else {

                uint16_t cur_frame = _mac->fr_mngr()->cur_frame();
                if (((_mac->address()->scalar() > othersMaxMacAddr) && (cur_frame % 2 == 0))
                        || ((_mac->address()->scalar() < othersMinMacAddr) && (cur_frame % 2 == 1)) )

                    ready_to_tx = true;
#if 0
                if (frameNumber & 1 && macCmp(mac_addr, othersMaxMacAddr1) == 1)
                    ready_to_tx = true;
                else if (frameNumber & 1 == 0 && macCmp(mac_addr, othersMinMacAddr1) == 1)
                    ready_to_tx = true;
#endif
            }
        }
    }
    DEBUG("_nent_tx_queue size = %u, ready_to_tx = %d\n", _nent_tx_queue.size(), ready_to_tx);

    sponsorsState       = sprUnavailable;
    othersState         = sprAvailable;
    othersMaxMacAddr    = 0;
    othersMinMacAddr    = ULONG_MAX;    // BUG (Only 32-bit)

    memset(othersMaxMacAddr1, 0x00, 6);
    memset(othersMinMacAddr1, 0xff, 6);

    MSH_NENT* nent = NULL;

    if (ready_to_tx) {
        nent = _nent_tx_queue.front();
        _nent_tx_queue.pop();

        /* restart Timer T25 if needed */
        /* new node part */
        if (nent->getEntryType() == 2 || nent->getEntryType() == 3)
            _reset_next_nent_attempt_opp();
    }
    return nent;
}

void
Net_entry_mngr::stop_sponsoring_due_to_recv_node_id(uint16_t tx_node_id)
{
    if (sponsoringState == sprSendAck && sponsoredNodeID == tx_node_id) {

        sponsoringState = sprNone;
        isSponsoring    = false;
        INFO("[%03u]%s: Change the sponsorsState to sprNone\n",
                _mac->node_id(), __func__);

        if (_mac->data_scheduler()->free_nent_alloc(
                    Data_scheduler::NEW_NODE_ID,
                    Data_scheduler::SENT_NET_ENTRY) == false) {

            FATAL("The sponsor cannot find a corresponding allocation to free\n");
        }

        if (ncfg_entry_open_ie_p) {

            delete ncfg_entry_open_ie_p;
            ncfg_entry_open_ie_p = NULL;
        }

        sponsoring_retry_cnt = 0;
        _cancel_next_nent_attempt_opp();
        memset(sponsoredMacAddr, 0, 6); /* reset the MAC address of candidate node for future use. */
    }
}

void
Net_entry_mngr::proc_ncfg_network_entry_sync(
        const MSH_NCFG& ncfg, uint16_t tx_node_id)
{
    if (!targetSponsor) {
        targetSponsor = _select_sponsor();
        INFO("[%05u]%s: selected target sponsor = %d\n",
                _mac->get_nid(), __func__, targetSponsor?targetSponsor->node_id():-1);

        /*
         * FIXME: this shortcut return statement should co-work with
         * _use_nexthop_to_bs_as_sponsor_node_flag being turn on.
         */
        if (_use_nexthop_to_bs_as_sponsor_node_flag) {
            if (!targetSponsor) {
#if 0
                NSLOBJ_INFO("Ignore the current sponsor node.\n");
                set_net_entry_sync();
                _mac->_state = MSH_INIT;
                //assert(0);
#endif
		return;
            }
        }
    }

    if (!targetSponsor || tx_node_id != targetSponsor->node_id())
        return;

    _mac->set_net_entry_contend();

    /*
     * Part of the NetEntry scheduling protocol.
     */
    sponsorsState = sprUnavailable;
    othersState = sprBusy;
    othersMaxMacAddr = ULONG_MAX;   // BUG (Only 32-bit)
    othersMinMacAddr = 0;
    _next_nent_attempt_opp = 0;

    _enqueue_nent(MSH_NENT::NET_ENTRY_REQUEST);
    _state = NET_ENTRY_OPEN;

    uint16_t    cur_frame;
    u_char      slot_no;
    u_char      sync_hop_cnt;
    ncfg.getTimeStamp(cur_frame, slot_no, sync_hop_cnt);

    /*
     * Synchronize my system time with the candidate sponsor node.
     */
    _mac->fr_mngr()->set_cur_frame(cur_frame);
    _sync_hop_count = sync_hop_cnt + 1;

    /*
     * FIXME: The number of minislots and the minislot size are not accurate.
     */
    _mac->fr_mngr()->set_nf_slot(221);
    _mac->fr_mngr()->set_slot_size(3);

    _mac->data_scheduler()->start();

    _mac->start_pseudo_scheduler();

    _mac->find_time_sync(slot_no);

    STAT("[%03u]%s: \e[1;35mstarts the network entry procedure\e[m, cur_frame = %#03x\n",
            _mac->get_nid(), __func__, _mac->fr_mngr()->cur_frame());

    if (!_nent_log->is_start())
        _nent_log->set_start_tick(GetCurrentTime());
}

void
Net_entry_mngr::proc_ncfg_network_entry_contend(
        const MSH_NCFG& ncfg, uint16_t tx_node_id)
{
    /*
     * Part of the NetEntry scheduling protocol.
     */
    Mac_address net_entry_mac_addr;
    ncfg.getEntryAddress(net_entry_mac_addr);

    DEBUG("%s: tx_node_id = %u, sponsor = %u, "
            "mac_addr = %s, net_entry_mac_addr = %s\n",
            __func__, tx_node_id, targetSponsor->node_id(),
            _mac->address()->str(), net_entry_mac_addr.str());

    if (tx_node_id == targetSponsor->node_id()) {

        if (net_entry_mac_addr.scalar() == 0) {

            sponsorsState = sprAvailable;
            DEBUG("%s: sponsorsState => sprAvailable\n", __func__);
        }
        else if (net_entry_mac_addr == *_mac->address()) {

            sponsorsState = sprPolling;
            DEBUG("%s: sponsorsState => sprPolling\n", __func__);
        }
    } else {

        if (net_entry_mac_addr.scalar() != 0) {

            othersState = sprBusy;
            DEBUG("%s: othersState => sprBusy\n", __func__);

            if (othersMaxMacAddr < net_entry_mac_addr.scalar())
                othersMaxMacAddr = net_entry_mac_addr.scalar();

            if (othersMinMacAddr > net_entry_mac_addr.scalar())
                othersMinMacAddr = net_entry_mac_addr.scalar();
        }
    }
}

bool
Net_entry_mngr::proc_ncfg_network_entry_open(uint16_t tx_node_id)
{
    if (_state != NET_ENTRY_OPEN || tx_node_id != targetSponsor->node_id())
        return false;

    INFO("[%03u]%s: target sponsor: %u, "
            "NET_ENTRY_OPEN => NET_ENTRY_NEG_CAP, send NENT ACK\n",
            _mac->get_nid(), __func__, tx_node_id);

    _state = NET_ENTRY_NEG_CAP;
    _cancel_next_nent_attempt_opp();

    _enqueue_nent(MSH_NENT::NET_ENTRY_ACK);

    return true;
}

bool
Net_entry_mngr::proc_ncfg_network_entry_ack(uint16_t tx_node_id)
{
    if (tx_node_id != targetSponsor->node_id())
        return false;

    _change_route_to_bs();

    targetSponsor = NULL;
    _cancel_next_nent_attempt_opp();

    if (!_nent_log->is_end())
        _nent_log->set_end_tick(GetCurrentTime());

    return true;
}

void
Net_entry_mngr::proc_ncfg_neighbor_link_est(
        Neighbor* tx_node, Msh_ncfg::Link_est_ie* ncfg_link_est_ie)
{
    uint32_t    auth_value;
    uint8_t     link_id;

    switch (ncfg_link_est_ie->getActionCode()) {

        case Msh_ncfg::Link_est_ie::ACTCODE_CHALLENGE:
            auth_value = ncfg_link_est_ie->getAuthValue();

            if (auth_value == _mac->node_id()) {    // Fake HMAC checking

                VINFO("Receive Challenge from NODE %u: auth_value=%d\n",
                        tx_node->node_id(), auth_value);

                if (tx_node && (
                            tx_node->is_link_wait_challenge() ||
                            tx_node->is_link_sent_challenge() ||
                            tx_node->is_link_wait_response()))
                    tx_node->set_link_sent_response();

                /*
                 * Record the start time of doLink protocol with this neighbor.
                 */
                tx_node->link_est_log()->set_start_tick(GetCurrentTime());
            }

            break;

        case Msh_ncfg::Link_est_ie::ACTCODE_CHALLENGE_RSP:

            auth_value = ncfg_link_est_ie->getAuthValue();
            link_id = ncfg_link_est_ie->getLinkID();

            if (auth_value == _mac->node_id()) {    // Fake HMAC checking


                if (tx_node && (
                            tx_node->is_link_wait_response() ||
                            tx_node->is_link_active())) {

                    INFO("[%03u]%s: Receive Challenge Response from node %u: auth_value=%d, link_id=%d\n",
                            _mac->node_id(), __func__, tx_node->node_id(), auth_value, link_id);

                    tx_node->set_link_sent_accept();

                    if (!tx_node->tx_link())
                        _create_rx_link(tx_node, link_id);
                }
            }
            break;

        case Msh_ncfg::Link_est_ie::ACTCODE_ACCEPT:

            link_id = ncfg_link_est_ie->getLinkID();

            if (tx_node && tx_node->is_link_wait_accept()) {

                if (_link_est_protocol_mode == extended_link_est_mode) {
                    DEBUG("Receive Accept from auth_value=%d\n",
                            ncfg_link_est_ie->getAuthValue());
                    /*
                     * Check whether this Accept message is destined to me.
                     */
                    if (ncfg_link_est_ie->getAuthValue() != _mac->node_id())
                        break;
                }

                INFO("[%03u]%s: Receive Accept from node %u: link_id=%d\n",
                        _mac->node_id(), __func__, tx_node->node_id(), link_id);

                tx_node->set_link_active();

                if (!tx_node->tx_link())
                    _create_rx_link(tx_node, link_id);

                /*
                 * To record the finish time of doLink protocol with this neighbor.
                 */
                tx_node->link_est_log()->set_end_tick(GetCurrentTime());

                _set_link_est_finished();
            }

            break;

        case Msh_ncfg::Link_est_ie::ACTCODE_REJECT:
            FATAL("Receive Reject\n");
            assert(0);

        default:
            FATAL("Unknown action code.\n");
            assert(0);
    }
}

void
Net_entry_mngr::proc_nent(mgmt_msg* recvmm, uint16_t tx_node_id, int len, timerObj* timerFrame)
{
    if (!_mac->is_operational())
        return;

    DEBUG("frame_start_tick = \e[1;36m%llu\e[m\n",
            _mac->fr_mngr()->frame_start_tick());
    ASSERT(timerFrame->expire() > GetCurrentTime(),
            "expire tick = %llu, cur_tick = %llu\n",
            timerFrame->expire(), GetCurrentTime());

    MSH_NENT nent((u_char*)recvmm, len - 1);

    INFO("[%03u]%s: tx_node_id = %u, type = %u, sponsor Node ID = %u\n",
            _mac->node_id(), __func__, tx_node_id,
            nent.getEntryType(), nent.getSponsorID());

    switch (nent.getEntryType()) {

    case MSH_NENT::NET_ENTRY_ACK:       _proc_nent_network_entry_ack(nent); break;
    case MSH_NENT::NET_ENTRY_REQUEST:   _proc_nent_network_entry_req(nent); break;
    case MSH_NENT::NET_ENTRY_CLOSE:     _proc_nent_network_entry_close(nent, tx_node_id); break;
    default: assert(0);
    }
}

ifmgmt*
Net_entry_mngr::proc_sbcreq(mgmt_msg* recvmm, int len)
{
    ifmgmt *ifmm;
    uint8_t t = 0;

    u_char *ptr;
    u_char fbas, fmaxpower[4], fctpower, ffftsize, fdemod, fmod, ffcs, ftc;
    u_int16_t fstg;
    u_char vmac[6];

    DEBUG("mm=%p, len=%d\n", recvmm, len);
    ptr = recvmm->msg;
    ifmm = new ifmgmt((u_char *) recvmm, len, 0);

    while ((t = ifmm->getNextType()) != 0) {
        DEBUG("Type = %d\t", t);
        switch (t) {
        case 1:
            ifmm->getNextValue(&fbas);
            DEBUG("bandwidth allocation = %d\n", fbas);
            break;
        case 2:
            ifmm->getNextValue(&fstg);
            DEBUG("stg = %d\n", fstg);
            break;
        case 3:
            ifmm->getNextValue(fmaxpower);
            DEBUG("maxpower = %d %d %d %d\n", fmaxpower[0],
                fmaxpower[1], fmaxpower[2], fmaxpower[3]);
            break;
        case 18:
            // FIXME if this is fixable.
            ifmm->getNextValue(vmac);
            if (memcmp(vmac, sponsoredMacAddr, 6) != 0) {
                delete ifmm;
                return NULL;
            }
            break;
        case 147:
            fctpower = 0;
            ifmm->getNextValue(&fctpower);
            DEBUG("current power = %d\n", fctpower);
            break;
        case 150:
            ffftsize = 0;
            ifmm->getNextValue(&ffftsize);
            DEBUG("current power = %d\n", ffftsize);
            break;
        case 151:
            fdemod = 0;
            ifmm->getNextValue(&fdemod);
            DEBUG("demodulator = %d\n", fdemod);
            break;
        case 152:
            fmod = 0;
            ifmm->getNextValue(&fmod);
            DEBUG("modulator = %d\n", fmod);
            break;
        case 153:
            ffcs = 0;
            ifmm->getNextValue(&ffcs);
            DEBUG("focus contention = %d\n", ffcs);
            break;
        case 154:
            ftc = 0;
            ifmm->getNextValue(&ftc);
            DEBUG("TC = %d\n", ftc);
            break;

        default:
            break;
        }
    }
    delete ifmm;

    ifmm = new ifmgmt(MG_SBCRSP, 0);    // Sec 11.8
    ifmm->appendTLV(1, 1, fbas);
    ifmm->appendTLV(2, 2, fstg);
    ifmm->appendTLV(3, 4, fmaxpower);
    ifmm->appendTLV(147, 1, fctpower);
    ifmm->appendTLV(150, 1, ffftsize);
    ifmm->appendTLV(151, 1, fdemod);
    ifmm->appendTLV(152, 1, fmod);
    ifmm->appendTLV(153, 1, ffcs);
    ifmm->appendTLV(154, 1, ftc);

    if (ifmm)
        _mac->sponsor_connection()->Insert(ifmm);

    return ifmm;
}

bool
Net_entry_mngr::proc_sbcrsp()
{
    /*
     * Successfully receive Sponsor Channel Response.
     */
    _mac->timer_mngr()->cancel_t(18);

    /*
     * The procedures of Security Authentication is skipped so far,
     * and jump to Register procedure.
     */
    _state = NET_ENTRY_REGISTER;

    INFO("[%03u]%s: NET_ENTRY_NEG_CAP => NET_ENTRY_REGISTER\n",
            _mac->get_nid(), __func__);

    return true;
}

Packet*
Net_entry_mngr::proc_regreq(mgmt_msg* recvmm, int len)
{
    int t = 0;
    u_char fhmac[21];

    ifmgmt ifmm((u_char*)recvmm, len, 0);

    while ((t = ifmm.getNextType()) != 0) {

        switch (t) {

        case 149:
            ifmm.getNextValue(fhmac);
            INFO("[%03u]%s: recv MG_REGREQ from node %02x:%02x:%02x:%02x:%02x:%02x\n",
                    _mac->node_id(), __func__,
                    fhmac[0], fhmac[1], fhmac[2],
                    fhmac[3], fhmac[4], fhmac[5]);

            if (memcmp(fhmac, sponsoredMacAddr, 6) != 0)
                return NULL;

            break;

        default:
            break;
        }
    }

    /*
     * The second argument of the tunnel() call is an IP
     * address of host order, which will be converted into
     * network order in the tunnel() function.
     */
    const in_addr* bs_ip = _mac->network_des()->get_bs_ip();
    ASSERT(bs_ip, "Mesh base staion's IP is missed.\n");
    return tunnel(ifmm, bs_ip->s_addr, _mac->inet_address());
}

Packet*
Net_entry_mngr::tunnel(ifmgmt& ifmm, in_addr_t dstip, in_addr_t srcip)
{
    in_addr inet_addr;
    inet_addr.s_addr = dstip;
    INFO("[%03u]%s: message to %s ",
            _mac->node_id(), __func__, inet_ntoa(inet_addr));
    inet_addr.s_addr = srcip;
    printf("from %s\n", inet_ntoa(inet_addr));

    struct ip *ih;
    struct udphdr *uh;
    char *buf;
    u_char *payload;
    int total_len =
        1 + ifmm.getLen() + sizeof(struct ip) + sizeof(struct udphdr);

    Packet *Pkt = new Packet();
    buf = Pkt->pkt_sattach(total_len);
    Pkt->pkt_sprepend(buf, total_len);
    ih = reinterpret_cast < struct ip *>(buf);
    uh = reinterpret_cast < struct udphdr *>(buf + sizeof(struct ip));
    payload = (u_char*)buf + sizeof(struct ip) + sizeof(struct udphdr);

    payload[0] = 1; // Tunnel subheader
    ifmm.copyTo(payload + 1);

    uh->source = 54707; // my port (sponsor)
    uh->dest = 54707;   // dest port
    uh->len = ifmm.getLen() + sizeof(struct udphdr);    // Total UDP datagram length
    uh->check = 0;      // Skip Checksum set to zero

    ih->ip_v = 0x4;
    ih->ip_hl = 0x5;
    ih->ip_tos = 0;
    ih->ip_len = total_len;
    ih->ip_id = 0;
    ih->ip_off = IP_DF;
    ih->ip_ttl = 0x40;
    ih->ip_p = IPPROTO_UDP;
    ih->ip_src = srcip;
    ih->ip_dst = dstip;
    ih->ip_sum = 0x00;  /* checksum */
    ip_sum_calc(sizeof(struct ip), (char *) ih);

#if 0
    for(int i=0; i<total_len; i++)
    {
        printf("%02x ", buf[i]&0xff);
        if((i+1)%12==0)
            printf("\n");
    }
    printf("\n");
#endif


#if 0
    INFO("%s: Inspect outgoing packet: iphdr information: \n", __func__);
    printf("\tip src =");
    showip(ih->ip_src);
    printf(", ip dst =");
    showip(ih->ip_dst);
    printf("\n");
#endif

    return Pkt;
}

#if 0
Packet*
mac802_16_MeshSS::tunnel(struct mgmt_msg* recvmm, uint32_t dstip, int len)
{
    struct ether_header *eh;
    struct ip *ih;
    struct udphdr *uh;
    char *buf, *payload;
    int total_len = 1 + len + sizeof(struct ip) + sizeof(struct udphdr);

    Packet *Pkt = new Packet();
    buf = Pkt->pkt_sattach(total_len);
    Pkt->pkt_sprepend(buf, total_len);
    ih = reinterpret_cast < struct ip *>(buf);
    uh = reinterpret_cast < struct udphdr *>(buf + sizeof(struct ip));
    payload = buf + sizeof(struct ip) + sizeof(struct udphdr);

    payload[0] = 1; // Tunnel subheader
    bcopy(recvmm, payload + 1, len);

    uh->source = 54707; // my port (sponsor)
    uh->dest = 54707;   // dest port
    uh->len = len + sizeof(struct udphdr);  // Total UDP datagram length
    uh->check = 0;      // Skip Checksum set to zero

    ih->ip_v = 0x4;
    ih->ip_hl = 0x5;
    ih->ip_tos = 0;
    ih->ip_len = total_len;
    ih->ip_id = 0;
    ih->ip_off = IP_DF;
    ih->ip_ttl = 0x40;
    ih->ip_p = IPPROTO_UDP;
    ih->ip_src = getInetAddress();
    ih->ip_dst = htonl(dstip);
    ih->ip_sum = 0x00;  /* checksum */
    ip_sum_calc(sizeof(struct ip), (char *) ih);

    Pkt->pkt_sprepend(buf, sizeof(struct ether_header));
    eh = reinterpret_cast <struct ether_header *>(Pkt->pkt_malloc(sizeof(struct ether_header)));

    return Pkt;
}
#endif

mgmt_msg*
Net_entry_mngr::extract(
        char* data, int& len, in_addr_t& srcip, in_addr_t local_ip)
{
    ip*     ih;
    udphdr* uh;
    char*   payload;

#if 0
    for(int z=0;z<40; z++)
    {
        printf("%02x%c", data[z]&0xff, (z+1)%32?' ':'\n');
    }
#endif

    ih = reinterpret_cast<struct ip*>(data);
    if (ih->ip_p != IPPROTO_UDP)
        return NULL;
    if (ih->ip_dst != local_ip) // local
        return NULL;

    uh = reinterpret_cast<struct udphdr*>(data + (ih->ip_hl << 2));
    if (uh->dest != 54707)
        return NULL;

    payload = data + sizeof(struct ip) + sizeof(struct udphdr);
    if (payload[0] == 1) {
        payload++;
        srcip = ih->ip_src;
        len = uh->len - sizeof(struct udphdr);
        return reinterpret_cast<struct mgmt_msg*>(payload);
    }
    return NULL;
}

uint16_t
Net_entry_mngr::proc_regrsp(mgmt_msg* recvmm, int len)
{
    int t = 0;
    u_char fhmac[21], fmanage, fipm, farq, fcrc;
    uint16_t fncid;
    uint16_t node_id = 0;

    ifmgmt ifmm((u_char*)recvmm, len, 0);

    while ((t = ifmm.getNextType()) != 0) {

        DEBUG("Type = %d\t", t);

        switch (t) {

        case 2:
            ifmm.getNextValue(&fmanage);
            DEBUG("manage = %d\n", fmanage);
            break;

        case 3:
            ifmm.getNextValue(&fipm);
            DEBUG("IP mode = %d\n", fipm);
            break;

        case 6:
            ifmm.getNextValue(&fncid);
            DEBUG("# of UCID = %d\n", fncid);
            break;

        case 10:
            ifmm.getNextValue(&farq);
            DEBUG("ARQ Support = %d\n", farq);
            break;

        case 12:
            ifmm.getNextValue(&fcrc);
            DEBUG("CRC Support = %d\n", fcrc);
            break;

        case 19:
            ifmm.getNextValue(&node_id);
            DEBUG("Node ID = %d\n", node_id);
            /*
             * REG-RSP should contain Node ID.
             */
            assert(node_id);
            break;

        case 149:
            ifmm.getNextValue(fhmac);
            DEBUG("HMAC = ...\n");

            if (memcmp(fhmac, _mac->address()->buf(), 6) != 0)
                return 0;

            break;

        default:
            FATAL("Non-implemented Type (%d)\n", t);
            break;
        }
    }

    //_state = (MeshState) (_state & ~NET_ENTRY_REGISTER);
    _state = NET_ENTRY_INIT;

    _mac->timer_mngr()->cancel_t(6);

    _mac->set_node_id(node_id);
    _mac->ctrl_connection()->SetSrcNodeID(node_id);

    INFO("[%03u]%s: CtrlConnection is initiated\n", node_id, __func__);

    assert(_mac->is_net_entry_contend());

    _enqueue_nent(MSH_NENT::NET_ENTRY_CLOSE);

    return node_id;
}

void
Net_entry_mngr::send_regreq()
{
    INFO("[%03u]%s: sponsor Node ID: %u\n",
            _mac->get_nid(), __func__, targetSponsor->node_id());

    u_char fhmac[21];
    ifmgmt *ifmm = new ifmgmt(MG_REGREQ, 0);

    memset(fhmac, 0, 21);
    _mac->address()->copy_to(fhmac, 6);

    ifmm->appendTLV(149, 21, fhmac);    // HMAC tuple

    // For PMP operation, the REG-REQ shall contain following three TLVs:
    ifmm->appendTLV(2, 1, 0U);  // SS management support 11.7.2
    ifmm->appendTLV(3, 1, 0U);  // IP management mode 11.7.3
    ifmm->appendTLV(6, 2, 3);   // Number of uplink CID supported
//      ifmm->appendTLV(10, 1, AttrARQ);        // ARQ Support
//      ifmm->appendTLV(12, 1, AttrCRC);        // MAC CRC support
    ifmm->appendTLV(18, 6, _mac->address()->buf());

    _mac->sponsor_connection()->Insert(ifmm);

//      ifmm->copyTo(saved_msg);
//      ifsavedmm = new ifmgmt(saved_msg, ifmm->getLen(), ifmm->getFLen());
//      lastCID = PriCID;

    _mac->timer_mngr()->reset_t(6);
}

void
Net_entry_mngr::send_sbcreq()
{
    ifmgmt *ifmm;

    // Send SBC-REQ
    INFO("[%03u]%s: target sponsor node ID: %u\n",
            _mac->get_nid(), __func__, targetSponsor->node_id());

    ifmm = new ifmgmt(MG_SBCREQ, 0);

    ifmm->appendTLV(1, 1, 0U);  // Bandwidth Allocation Support 11.8.1

    ifmm->appendTLV(2, 2, 10);  // Subscriber transition gaps 11.8.3.1
    ifmm->appendTLV(3, 4, 0xffffffff);  // Maximum transmit power 11.8.3.2
    ifmm->appendTLV(147, 1, 0xff);  // Current transmit power 11.8.3.3

    // OFDM specfic parameters 11.8.3.6
    ifmm->appendTLV(150, 1, 1); // FFT-256
    ifmm->appendTLV(151, 1, 1); // 64-QAM
    ifmm->appendTLV(152, 1, 1); // 64-QAM
    ifmm->appendTLV(153, 1, 0U);
    ifmm->appendTLV(154, 1, 0U);
    ifmm->appendTLV(18, 6, _mac->address()->buf());   // FIXME if this is fixable.

//  Push to Sponsor Channel
    _mac->sponsor_connection()->Insert(ifmm);
//      ifsavedmm = new ifmgmt(&saved_msg, ifmm->getLen(), ifmm->getFLen());
//      lastCID = BasicCID;

    _mac->timer_mngr()->reset_t(18);
}

void
Net_entry_mngr::append_sponsoring_info(MSH_NCFG* ncfg)
{
    if (!is_sponsoring())
        return;

    ncfg->setEntryAddress(sponsoredMacAddr);

    if (sponsoringState == sprSendOpenGrant) {

        Alloc_base* alloc = NULL;
        if ((alloc = _mac->data_scheduler()->get_nent_alloc(5))) {

            // Network Entry Open
            Msh_ncfg::Net_entry_open_ie* entryOpenIE = new Msh_ncfg::Net_entry_open_ie(
                    alloc->slot_start(), alloc->slot_range(),
                    alloc->frame_start(), 0, 0, alloc->frame_range(),
                    (int)estPropDelay);

            /* C.C. Lin: duplicate entry OpenIE:
             * ugly codes! it should be a uniform API to copy IE from
             * another one.
             */
            if (ncfg_entry_open_ie_p) {

                delete ncfg_entry_open_ie_p;
                ncfg_entry_open_ie_p = NULL;

            }

            ncfg_entry_open_ie_p = new Msh_ncfg::Net_entry_open_ie(
                    alloc->slot_start(), alloc->slot_range(),
                    alloc->frame_start(), 0, 0, alloc->frame_range(),
                    (int)estPropDelay);
#if 1
            INFO("[%03u]%s: send NetEntryOpenIE with alloc:\n",
                    _mac->node_id(), __func__);
            INFO_FUNC(alloc->dump());
#endif
            ncfg->addEmbedded(entryOpenIE);

            sponsoringState = sprRunning;
            _reset_next_nent_attempt_opp();

            delete alloc;
        }
        else {

            FATAL("%s: The sponsor cannot find an allocation for the new node!\n", __func__);
        }
    }
    else if (sponsoringState == sprRunning) {

        /* Terminate the sponsorship. */
        if (sponsoring_retry_cnt >= default_max_sponsoring_retry_cnt) {

            sponsoringState = sprNone;
            isSponsoring = false;
            INFO("%s: Change the sponsorsState to sprNone\n", __func__);

            if (_mac->data_scheduler()->free_nent_alloc(
                        Data_scheduler::NEW_NODE_ID,
                        Data_scheduler::SENT_NET_ENTRY) == false) {

                FATAL("The sponsor cannot find a corresponding allocation to free\n");
            }

            if ( ncfg_entry_open_ie_p ) {

                delete ncfg_entry_open_ie_p;
                ncfg_entry_open_ie_p = NULL;
            }

            sponsoring_retry_cnt = 0;
            _cancel_next_nent_attempt_opp();
            memset(sponsoredMacAddr, 0, 6); /* reset the MAC address of candidate node for future use. */
        }

        /* T25 Timeout */
        if (_next_nent_attempt_opp == _mac->fr_mngr()->nent_opp_seq_end()) {

            ASSERT(ncfg_entry_open_ie_p,
                    "ncfg_entry_open_ie_p should not be null during retransmission phase. \n");

            INFO("%s: re-send NetEntryOpenIE: dump as follows\n", __func__);
            INFO_FUNC(ncfg_entry_open_ie_p->dump());


            Msh_ncfg::Net_entry_open_ie* tmp = new Msh_ncfg::Net_entry_open_ie(ncfg_entry_open_ie_p);

            ncfg->addEmbedded(ncfg_entry_open_ie_p);

            ncfg_entry_open_ie_p = tmp;

            ++sponsoring_retry_cnt;

            /* sponsor node part */
            _reset_next_nent_attempt_opp();
        }

    }
    else if (sponsoringState == sprSendAck) {

        // Network Entry Ack
        ncfg->addEmbedded(new Msh_ncfg::Net_entry_ack_ie());
    }
}

void
Net_entry_mngr::link_establish(MSH_NCFG* ncfg)
{
    if (!_link_est_log->is_start()) {
        /*
         * The first time to establish link protocol with 1-hop neighbors.
         */
        _link_est_log->set_start_tick(GetCurrentTime());
        STAT("[%03u]%s: \e[1;35mStart to establish link protocol\e[m\n",
                _mac->node_id(), __func__);
    }

    const Neighbor_mngr::map_t& neighbors = _mac->nbr_mngr()->neighbors();
    for (Neighbor_mngr::map_t::const_iterator it = neighbors.begin();
            it != neighbors.end() ; it++) {

        Neighbor* nbr = it->second;

        if (nbr->hop_count() != 1 )
            continue;

        if (nbr->is_link_active() &&
                _link_est_protocol_mode == original_link_est_mode) {

            /* In original mode, we should do this to progress the procedure. */
            continue;

        }
        else if (nbr->is_link_sent_challenge()) {

            INFO("[%03u]%s: sending challenge to Node %d, "
                    "SendChallenge => WaitResponse\n",
                    _mac->node_id(), __func__, nbr->node_id());

            ncfg->addEmbedded(new Msh_ncfg::Link_est_ie(
                        Msh_ncfg::Link_est_ie::ACTCODE_CHALLENGE,
                        nbr->node_id(),
                        0,
                        _link_est_ie_format));

            nbr->set_link_wait_response();

            /*
             * Record the start time of doLink protocol with this neighbor.
             */
            nbr->link_est_log()->set_start_tick(GetCurrentTime());

            if (_link_est_protocol_mode == original_link_est_mode)
                break;
        }
        else if (nbr->is_link_sent_response()) {


            /* C.C. Lin: check if we just need to resend a message but
             * not create a corresponding structure.
             */

            //Mesh_connection* data_conn_p = search_link(nbr->nodeId, node_id);

            if (nbr->rx_link()) {

                ncfg->addEmbedded(new Msh_ncfg::Link_est_ie(Msh_ncfg::Link_est_ie::ACTCODE_CHALLENGE_RSP,
                            nbr->node_id(),
                            nbr->rx_link()->id(),
                            _link_est_ie_format));

                nbr->set_link_wait_accept();

                INFO("[%03u]%s: sending response to Node %d, link = (%d,%d), WaitResponse => WaitAccept\n",
                        _mac->node_id(), __func__, nbr->node_id(),
                        nbr->rx_link()->id(),
                        nbr->tx_link()?nbr->tx_link()->id():Mesh_link::UNKNOWN_ID);

                if ( _link_est_protocol_mode == original_link_est_mode )
                    break;
            }
            else {

                _create_tx_link(nbr);

                ncfg->addEmbedded(new Msh_ncfg::Link_est_ie(Msh_ncfg::Link_est_ie::ACTCODE_CHALLENGE_RSP,
                            nbr->node_id(),
                            nbr->rx_link()->id(),
                            _link_est_ie_format));

                nbr->set_link_wait_accept();

                INFO("[%03u]%s: sending response to Node %d, link = (%d,%d), WaitResponse => WaitAccept\n",
                        _mac->node_id(), __func__, nbr->node_id(),
                        nbr->rx_link()->id(),
                        nbr->tx_link()?nbr->tx_link()->id():Mesh_link::UNKNOWN_ID);

                if (_link_est_protocol_mode == original_link_est_mode)
                    break;
            }
        }
        else if (nbr->is_link_sent_accept()) {
            /*
             * Check if we just need to resend a message but
             * not create a corresponding structure.
             */

            if (nbr->rx_link()) {

                ncfg->addEmbedded(new Msh_ncfg::Link_est_ie(
                            Msh_ncfg::Link_est_ie::ACTCODE_ACCEPT,
                            nbr->node_id(),
                            nbr->rx_link()->id(),
                            _link_est_ie_format));

                nbr->set_link_active();

                INFO("[%03u]%s: sending accept to Node %d, link = (%d,%d), WaitAccept => Active\n",
                        _mac->node_id(), __func__, nbr->node_id(),
                        nbr->rx_link()->id(),
                        nbr->tx_link()?nbr->tx_link()->id():Mesh_link::UNKNOWN_ID);

                if (_link_est_protocol_mode == original_link_est_mode)
                    break;
            }
            else {

                _create_tx_link(nbr);

                ncfg->addEmbedded(new Msh_ncfg::Link_est_ie(Msh_ncfg::Link_est_ie::ACTCODE_ACCEPT,
                            nbr->node_id(),
                            nbr->rx_link()->id(),
                            _link_est_ie_format));

                nbr->set_link_active();

                INFO("[%03u]%s: sending accept to Node %d, link = (%d,%d), WaitAccept => Active\n",
                        _mac->node_id(), __func__, nbr->node_id(), nbr->rx_link()->id(), nbr->tx_link()->id());

                /*
                 * To record the finish time of doLink protocol with this neighbor.
                 */
                nbr->link_est_log()->set_end_tick(GetCurrentTime());

                _set_link_est_finished();

                if (_link_est_protocol_mode == original_link_est_mode)
                    break;
            }

            DEBUG("dump Neighbor Info:\n");
            DEBUG_FUNC(nbr->dump());
        }
        else if (_link_est_protocol_mode == original_link_est_mode)
            break;
    }
}

Mesh_link_tx*
Net_entry_mngr::tx_link(uint8_t link_id)
{
    if (_tx_links.find(link_id) == _tx_links.end())
        return NULL;

    return _tx_links[link_id];
}

void
Net_entry_mngr::_reset_next_nent_attempt_opp()
{
    INFO("[%03u]%s: T25 re-start\n", _mac->get_nid(), __func__);
    _next_nent_attempt_opp = 0;
    _mac->timer_mngr()->reset_t(25);
}

void
Net_entry_mngr::_cancel_next_nent_attempt_opp()
{
    INFO("[%03u]%s: T25 canceled\n", _mac->get_nid(), __func__);
    _next_nent_attempt_opp = 0;
    _mac->timer_mngr()->cancel_t(25);
}

const Neighbor*
Net_entry_mngr::_select_sponsor() const
{
    if (_use_nexthop_to_bs_as_sponsor_node_flag) {

        const in_addr* bs_ip = _mac->network_des()->get_bs_ip();
        ASSERT(bs_ip, "Mesh base staion's IP is missed.\n");
        uint32_t nexthop_nid = _mac->route_module()->query_route(bs_ip->s_addr);

        ASSERT(nexthop_nid > 0, "no route to a BS node.\n");
        VINFO("[%03u]%s: Given Sponsor NID = %u \n",
                _mac->get_nid(), __func__, nexthop_nid);

        Neighbor* possible_sponsor = _mac->nbr_mngr()->neighbor(nexthop_nid);
        if (possible_sponsor)
            return possible_sponsor;

        WARN("[%03u]%s: Given sponsor node (node %u) has not yet been functional.\n",
                _mac->get_nid(), __func__, nexthop_nid);
        return NULL;

    }
    else {

        const Neighbor_mngr::map_t& neighbors = _mac->nbr_mngr()->neighbors();

        TEST("[%03u]%s: neighbor list size: %u\n",
                _mac->get_nid(), __func__, neighbors.size());

        if (neighbors.empty())
            return NULL;

        Neighbor* candidate_sponsor = NULL;
        Neighbor_mngr::map_t::const_iterator it;
        for (it = _mac->nbr_mngr()->neighbors().begin();
                it != _mac->nbr_mngr()->neighbors().end(); it++) {

            Neighbor* nbr = it->second;
            if (nbr->hop_count() == 1 &&
                    !nbr->has_been_selected_as_a_cs_node()) {

                candidate_sponsor = nbr;
                break;
            }
        }

        if (it != neighbors.end() ) {

            for (it++; it != neighbors.end(); it++) {

                Neighbor* nbr = it->second;
                if (nbr->hop_count() == 1 &&
                        nbr->sync_hop_count() < candidate_sponsor->sync_hop_count() &&
                        !nbr->has_been_selected_as_a_cs_node()) {

                    candidate_sponsor = nbr;
                }
            }
        }

        if (!candidate_sponsor)
            candidate_sponsor = neighbors.begin()->second;

        candidate_sponsor->select_as_a_cs_node();

        return candidate_sponsor;
    }
}

void
Net_entry_mngr::_change_route_to_bs() const
{
    if (!_change_route_to_bs_flag)
        return;

    const in_addr* bs_ip = _mac->network_des()->get_bs_ip();
    ASSERT(bs_ip, "Mesh base staion's IP is missed.\n");

    _mac->route_module()->change_route(bs_ip->s_addr, targetSponsor->node_id());

    INFO("[%03u]%s: target sponsor: %u\n",
            _mac->node_id(), __func__, targetSponsor->node_id());
}

/*
 * Enqueue an outgoing MSH-NENT message at the tail of the queue.
 */
void
Net_entry_mngr::_enqueue_nent(uint8_t entType)
{
    assert(targetSponsor);

    MSH_NENT* nent = new MSH_NENT(0, targetSponsor->node_id(), 15, 7);

    INFO("[%03u]%s: type = %u, target sponsor node ID: %u\n",
            _mac->get_nid(), __func__, entType, targetSponsor->node_id());

    if (entType == MSH_NENT::NET_ENTRY_ACK) {

        nent->setTypeAck(0);
    }
    else if (entType == MSH_NENT::NET_ENTRY_REQUEST) {

        nent->setTypeRequest(_mac->address(), (u_char*)"NCTUNS40", 300, 10);
    }
    else if (entType == MSH_NENT::NET_ENTRY_CLOSE) {

        nent->setTypeClose();
    }

    /*
    len = nent->getLen();
    i = nent->copyTo(Buffer);
    printf("NENT len=%d %d\n", len, i);
    for(i=0;i<len;i++)
    printf("%02x ", Buffer[i]&0xff);
    printf("\n");
    */
    _nent_tx_queue.push(nent);
}

void
Net_entry_mngr::_proc_nent_network_entry_ack(const MSH_NENT& nent)
{
    if (nent.getSponsorID() != _mac->node_id())
        return;

    INFO("[%03u]%s: candidate node: %02x:%02x:%02x:%02x:%02x:%02x\n",
            _mac->node_id(), __func__,
            sponsoredMacAddr[0], sponsoredMacAddr[1],
            sponsoredMacAddr[2], sponsoredMacAddr[3],
            sponsoredMacAddr[4], sponsoredMacAddr[5]);

    _cancel_next_nent_attempt_opp();
}

void
Net_entry_mngr::_proc_nent_network_entry_req(const MSH_NENT& nent)
{
    if (nent.getSponsorID() == _mac->node_id()) {

        if (is_sponsoring()) {
            INFO("[%03u]%s: sponsoring %02x:%02x:%02x:%02x:%02x:%02x\n",
                    _mac->node_id(),__func__,
                    sponsoredMacAddr[0], sponsoredMacAddr[1],
                    sponsoredMacAddr[2], sponsoredMacAddr[3],
                    sponsoredMacAddr[4], sponsoredMacAddr[5]);
#if 0
            if (sponsoringState == sprRunning &&
                    !memcmp(nent.getMacaddress(), sponsoredMacAddr, 6)) {
                /*
                 * If the sponsoring node receive `NetEntryRequest' from
                 * the candidate node after it had sent the NetEntryOpen,
                 * it means that the MSH-NCFG is lost. This condition is
                 * NOT allowed.
                 */
                assert(0);
            }
#endif
            return;
        }

        _mac->fr_mngr()->fix_prop_delay(1);
        TICK_TO_MICRO(estPropDelay, _mac->fr_mngr()->prop_delay_tick());

        isSponsoring    = true;
        sponsoringState = sprSendOpenGrant;
        memcpy(sponsoredMacAddr, nent.getMacaddress(), 6);

        INFO("[%03u]%s: new node: `%02x:%02x:%02x:%02x:%02x:%02x', "
                "estPropDelay = %ld, delay_in_tick = %llu\n",
                _mac->node_id(), __func__,
                ((uint8_t*)nent.getMacaddress())[0],
                ((uint8_t*)nent.getMacaddress())[1],
                ((uint8_t*)nent.getMacaddress())[2],
                ((uint8_t*)nent.getMacaddress())[3],
                ((uint8_t*)nent.getMacaddress())[4],
                ((uint8_t*)nent.getMacaddress())[5],
                (long)estPropDelay,
                _mac->fr_mngr()->prop_delay_tick());


    } else if (_network_entry_process_mode == enhanced_nent_process_mode) {

        /*
         * In enhanced NENT mode, Learn the information of a two-hop node from the
         * candidate sponsoring node field contained in an MSH-NENT message. This
         * new node is a potentially-competing node with two-hop distance, and if we
         * can compute the next_tx_opp() with this node, the possibility of MSH-NCFG
         * messages' collision will be reduced.
         */
        Neighbor* nbr = new Neighbor(nent.getSponsorID(), 2);

        if (_mac->nbr_mngr()->add_neighbor(nbr))
            VINFO("[%03u]%s: Add neighbor (nid: %u)\n",
                    _mac->node_id(), __func__, nent.getSponsorID());


        /* C.C. Lin: yield one transmission opportunity for the
         * new and sponsoring nodes each time when receiving
         * a MSH-NENT request. This yielding is required every time
         * when a new node sends a MSH-NENT::NetworkEntryReq because
         * an operational cannot assume that the candiate sponsor
         * node will transmit its MSH-NCFG::NetEntryOpen to the
         * new node successfully. Since this action may fail, the
         * only safe way to guarantee the Network Entry procedure
         * is to yield one transmission opportunity every time listening
         * to a transmission of MSH-NENT::NetworkEntryReq. Of course,
         * this scheme may hurt the system performance slightly at
         * intialization phase, but it guarantees the initlization
         * will succeed.
         */
        if (_mac->ncfg_scheduler()->is_valid_next_tx_opp())
            _mac->reschedule_next_ncfg(_mac->fr_mngr()->max_ncfg_tx_opp());
    }
}

void
Net_entry_mngr::_proc_nent_network_entry_close(const MSH_NENT& nent, uint16_t tx_node_id)
{
    Neighbor* nbr = new Neighbor(tx_node_id, 1);

    nbr->set_link_wait_challenge();

    if (_mac->nbr_mngr()->add_neighbor(nbr)) {

        VINFO("[%03u]%s: Add neighbor (nid: %u)\n",
                _mac->node_id(), __func__, nbr->node_id());
    } 

    if (nent.getSponsorID() != _mac->node_id() || !is_sponsoring())
        return;

    INFO("[%03u]%s: Got NetEntryClose, sponsoringState => sprSendAck\n",
            _mac->node_id(), __func__);

    sponsoringState = sprSendAck;
    sponsoredNodeID = tx_node_id;
}

void
Net_entry_mngr::_create_rx_link(class Neighbor* peer, uint8_t peer_tx_link_id)
{
    /*
     * The link creation should not fail here.
     */
    Mesh_link_rx* rx_link;
    assert(rx_link = peer->create_tx_link(peer_tx_link_id, _mac->node_id()));

    /*
     * FIXME: The operation would crash if there are multiple links
     *        for the same node.
     */
    assert(!peer->tx_link());
    peer->set_tx_link(rx_link);


    /*
     * Create connections.
     */
    assert(rx_link->create_connection(
                Mesh_connection::gen_id(peer_tx_link_id,
                    Mesh_connection::TYPE_MAC_MANAGEMENT,
                    Mesh_connection::RELIABILITY_NO_RE_XMIT,
                    Mesh_connection::CLASS_GENERAL, 0)));
    assert(rx_link->create_connection(
                Mesh_connection::gen_id(peer_tx_link_id,
                    Mesh_connection::TYPE_IP,
                    Mesh_connection::RELIABILITY_NO_RE_XMIT,
                    Mesh_connection::CLASS_GENERAL, 0)));
}

void
Net_entry_mngr::_create_tx_link(Neighbor* peer)
{
    uint8_t link_id = _mac->nbr_mngr()->max_rx_link_id() + 1;
    assert(link_id != Mesh_link::UNKNOWN_ID);
    /*
     * Allocate transmission link hash for corresponding link ID.
     */
    if (_tx_links.find(link_id) == _tx_links.end())
        _tx_links[link_id] = new Mesh_link_tx(link_id, peer, _mac->node_id());

    assert(!peer->rx_link());
    peer->set_rx_link(_tx_links[link_id]);


    /*
     * Create connections.
     */
    assert(_tx_links[link_id]->create_connection(
                Mesh_connection::gen_id(link_id,
                    Mesh_connection::TYPE_MAC_MANAGEMENT,
                    Mesh_connection::RELIABILITY_NO_RE_XMIT,
                    Mesh_connection::CLASS_GENERAL, 0)));
    assert(_tx_links[link_id]->create_connection(
                Mesh_connection::gen_id(link_id,
                    Mesh_connection::TYPE_IP,
                    Mesh_connection::RELIABILITY_NO_RE_XMIT,
                    Mesh_connection::CLASS_GENERAL, 0)));
}

/*
 * Check whether the last one neighbor's link have been established or not.
 * We will judge it by testing two conditions. One is upon node send
 * 'link ACCEPT', the other one is node receive 'link ACCEPT'.
 */
void
Net_entry_mngr::_set_link_est_finished()
{
    if (_link_est_log->is_end())
        return;

    if (_mac->nbr_mngr()->nf_active_link() ==
            _mac->node_deg_des()->nf_1_hop_neighbors()) {

        _link_est_log->set_end_tick(GetCurrentTime());

        STAT("[%03u]%s: \e[1;35mEnd to establish link protocol\e[m\n",
                _mac->node_id(), __func__);
        STAT_FUNC(fflush(stdout));

        update_node_status(TxOppUtilizationCounter::msh_ncfg,
                _mac->node_id(), node_stat_blk_t::functional_link_est);
        update_node_status(TxOppUtilizationCounter::msh_dsch,
                _mac->node_id(), node_stat_blk_t::functional_link_est);
    }
}


/*
 * Member function definitions of class `Nent_and_link_est_log'.
 */

/*
 * Constructor
 */
Nent_and_link_est_log::Nent_and_link_est_log()
: _start_tick(UNKNOWN_TICK)
, _end_tick(UNKNOWN_TICK)
{
}

/*
 * Destructor
 */
Nent_and_link_est_log::~Nent_and_link_est_log()
{
}

double
Nent_and_link_est_log::start_sec() const
{
    double sec;
    TICK_TO_SEC(sec, _start_tick);
    return sec;
}

double
Nent_and_link_est_log::end_sec() const
{
    double sec;
    TICK_TO_SEC(sec, _end_tick);
    return sec;
}

double
Nent_and_link_est_log::period_in_sec() const
{
    double sec;
    TICK_TO_SEC(sec, _end_tick - _start_tick);
    return sec;
}
